/**
 * useStore Hook - 表单状态管理自定义 Hook
 *
 * 功能概述：
 * 1. 提供表单字段的状态管理，包括字段值、验证状态、错误信息等
 * 2. 实现基于 async-validator 的数据验证功能
 * 3. 提供字段操作方法，如获取值、设置值、重置字段等
 * 4. 使用 useReducer 管理字段状态，useState 管理表单整体状态
 *
 * 设计模式：
 * - 使用 React Hooks 实现状态管理
 * - 使用 Reducer 模式管理字段状态变更
 * - 支持同步和异步验证规则
 * - 提供完整的表单操作方法
 */

import { useState, useReducer } from "react";
import Schema, { RuleItem, ValidateError } from "async-validator";
/**
 * 替代 lodash-es 的 mapValues 与 each，避免测试环境 ESM 解析问题
 */
function mapValues<T extends Record<string, any>, R>(obj: T, iteratee: (item: T[keyof T], key: string) => R): Record<string, R> {
  const res: Record<string, R> = {};
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      res[key] = iteratee(obj[key], key);
    }
  }
  return res;
}
function each<T extends Record<string, any>>(obj: T, iteratee: (value: T[keyof T], key: string) => void) {
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      iteratee(obj[key], key);
    }
  }
}

/**
 * 自定义规则函数类型
 * 支持动态规则，可以根据其他字段的值动态生成验证规则
 *
 * @param getFieldValue - 获取其他字段值的函数
 * @returns 验证规则对象
 */
export type CustomRuleFunc = ({
  getFieldValue,
}: {
  getFieldValue: (name: string) => any;
}) => RuleItem;

/**
 * 自定义规则类型
 * 可以是规则对象或规则函数，提供灵活的验证规则定义方式
 */
export type CustomRule = RuleItem | CustomRuleFunc;

/**
 * 单个字段的详细信息接口
 * 包含字段的所有状态信息
 */
export interface FieldDetail {
  name: string; // 字段名，唯一标识
  value: string; // 字段当前值
  rules: CustomRule[]; // 验证规则数组
  isValid: boolean; // 是否验证通过
  errors: ValidateError[]; // 验证错误信息数组
}

/**
 * 所有字段的状态对象接口
 * 使用键值对形式存储所有字段的状态
 */
export interface FieldsState {
  [key: string]: FieldDetail;
}

/**
 * 验证错误类型扩展
 * 继承 Error 类型，添加 async-validator 特定的错误信息
 */
export interface ValidateErrorType extends Error {
  errors: ValidateError[]; // 错误信息数组
  fields: Record<string, ValidateError[]>; // 按字段分组的错误信息
}

/**
 * 表单整体状态接口
 * 包含表单的全局状态信息
 */
export interface FormState {
  isValid: boolean; // 表单是否全部验证通过
  isSubmitting: boolean; // 表单是否正在提交
  errors: Record<string, ValidateError[]>; // 所有字段的错误信息
}

/**
 * 字段操作 Action 类型
 * 定义了对字段状态的所有操作类型
 */
export interface FieldsAction {
  type: "addField" | "updateValue" | "updateValidateResult"; // 操作类型
  name: string; // 字段名，标识操作的目标字段
  value: any; // 操作值，具体含义取决于操作类型
}

/**
 * 字段状态 Reducer 函数
 *
 * 功能：处理字段状态的变更操作
 * 设计模式：使用 Reducer 模式管理状态变更，确保状态更新的可预测性
 *
 * @param state - 当前字段状态
 * @param action - 操作指令
 * @returns 新的字段状态
 */
function fieldsReducer(state: FieldsState, action: FieldsAction): FieldsState {
  switch (action.type) {
    case "addField":
      /**
       * 添加新字段操作
       * 将新字段添加到状态中，如果字段已存在则覆盖
       */
      return {
        ...state, // 保留现有字段状态
        [action.name]: { ...action.value }, // 添加或更新指定字段
      };

    case "updateValue":
      /**
       * 更新字段值操作
       * 只更新指定字段的值，保留其他字段属性
       */
      return {
        ...state, // 保留现有字段状态
        [action.name]: {
          ...state[action.name], // 保留字段原有属性
          value: action.value, // 更新字段值
        },
      };

    case "updateValidateResult":
      /**
       * 更新字段验证结果操作
       * 更新指定字段的验证状态和错误信息
       */
      const { isValid, errors } = action.value;
      return {
        ...state, // 保留现有字段状态
        [action.name]: {
          ...state[action.name], // 保留字段原有属性
          isValid, // 更新验证状态
          errors, // 更新错误信息
        },
      };

    default:
      /**
       * 默认情况：返回原状态
       * 对于未知的操作类型，保持状态不变
       */
      return state;
  }
}

/**
 * useStore 自定义 Hook
 *
 * 功能：表单状态管理的核心逻辑，提供完整的表单操作和验证功能
 * 设计模式：使用 React Hooks 实现状态管理，分离状态和操作逻辑
 *
 * @param initialValues - 表单初始值，可选参数
 * @returns 表单状态管理相关方法和状态
 */
function useStore(initialValues?: Record<string, any>) {
  /**
   * 表单整体状态管理
   * 使用 useState 管理表单的全局状态
   */
  const [form, setForm] = useState<FormState>({
    isValid: true, // 初始验证状态为通过
    isSubmitting: false, // 初始提交状态为未提交
    errors: {}, // 初始错误信息为空对象
  });

  /**
   * 字段状态管理
   * 使用 useReducer 管理所有字段的状态，支持复杂的状态变更逻辑
   */
  const [fields, dispatch] = useReducer(fieldsReducer, {});

  /**
   * 获取单个字段的值
   *
   * @param key - 字段名
   * @returns 字段值，如果字段不存在则返回 undefined
   */
  const getFieldValue = (key: string) => {
    return fields[key] && fields[key].value;
  };

  /**
   * 获取所有字段的值
   *
   * 实现方式：使用 lodash 的 mapValues 函数提取每个字段的值
   *
   * @returns 包含所有字段值的对象，格式为 {字段名: 字段值}
   */
  const getFieldsValue = () => {
    return mapValues(fields, (item) => item.value);
  };

  /**
   * 设置单个字段的值
   *
   * 注意：只有当字段存在时才进行设置
   *
   * @param name - 字段名
   * @param value - 要设置的字段值
   */
  const setFieldValue = (name: string, value: any) => {
    if (fields[name]) {
      dispatch({ type: "updateValue", name, value });
    }
  };

  /**
   * 重置所有字段到初始值
   *
   * 处理流程：
   * 1. 检查是否有初始值
   * 2. 遍历初始值对象
   * 3. 对每个存在的字段重置为初始值
   */
  const resetFields = () => {
    if (initialValues) {
      each(initialValues, (value, name) => {
        if (fields[name]) {
          dispatch({ type: "updateValue", name, value });
        }
      });
    }
  };

  /**
   * 转换验证规则
   *
   * 功能：处理函数类型的规则，将其转换为规则对象
   * 设计目的：支持动态验证规则，可以根据其他字段的值动态生成规则
   *
   * @param rules - 原始规则数组，可能包含函数类型的规则
   * @returns 转换后的规则数组，所有规则都是对象类型
   */
  const transfromRules = (rules: CustomRule[]) => {
    return rules.map((rule) => {
      if (typeof rule === "function") {
        // 如果是函数规则，调用函数获取规则对象
        // 传入 getFieldValue 函数，允许规则访问其他字段的值
        const calledRule = rule({ getFieldValue });
        return calledRule;
      } else {
        // 普通规则对象直接返回
        return rule;
      }
    });
  };

  /**
   * 验证单个字段
   *
   * 功能：对指定字段进行验证，更新验证结果到状态
   * 实现方式：使用 async-validator 库进行验证
   *
   * 处理流程：
   * 1. 获取字段的值和规则
   * 2. 转换规则（处理函数规则）
   * 3. 创建验证描述符和值映射
   * 4. 执行验证
   * 5. 更新验证结果到状态
   *
   * @param name - 要验证的字段名
   */
  const validateField = async (name: string) => {
    const { value, rules } = fields[name];
    const afterRules = transfromRules(rules); // 转换规则

    /**
     * 创建验证描述符
     * async-validator 需要的验证规则描述符格式
     */
    const descriptor = {
      [name]: afterRules, // 字段名: 规则数组
    };

    /**
     * 创建值映射
     * async-validator 需要的验证数据格式
     */
    const valueMap = {
      [name]: value, // 字段名: 字段值
    };

    // 创建验证器实例
    const validator = new Schema(descriptor);
    let isValid = true; // 验证状态，默认为通过
    let errors: ValidateError[] = []; // 错误信息数组

    try {
      // 执行验证
      await validator.validate(valueMap);
    } catch (e) {
      // 验证失败，捕获错误信息
      isValid = false;
      const err = e as any;
      console.log("e", err.errors); // 开发调试日志
      console.log("fields", err.fields); // 开发调试日志
      errors = err.errors; // 获取错误信息
    } finally {
      console.log("errors", isValid); // 开发调试日志

      /**
       * 更新验证结果到状态
       * 无论验证成功还是失败，都需要更新状态
       */
      dispatch({
        type: "updateValidateResult",
        name,
        value: { isValid, errors },
      });
    }
  };

  /**
   * 验证所有字段
   *
   * 功能：对所有表单字段进行整体验证，主要用于表单提交时的验证
   * 实现方式：使用 async-validator 进行批量验证
   *
   * 处理流程：
   * 1. 获取所有字段的值
   * 2. 创建所有字段的验证描述符
   * 3. 执行整体验证
   * 4. 根据验证结果更新每个字段的状态
   * 5. 更新表单整体状态
   *
   * @returns 验证结果对象，包含验证状态、错误信息和字段值
   */
  const validateAllFields = async () => {
    let isValid = true; // 整体验证状态
    let errors: Record<string, ValidateError[]> = {}; // 错误信息对象

    /**
     * 获取所有字段的值
     * 格式：{'username': 'abc', 'password': '123'}
     */
    const valueMap = mapValues(fields, (item) => item.value);

    /**
     * 创建所有字段的验证描述符
     * 为每个字段创建转换后的规则数组
     */
    const descriptor = mapValues(fields, (item) => transfromRules(item.rules));
    const validator = new Schema(descriptor);

    /**
     * 设置表单提交状态
     * 标记表单正在提交中，可以用于显示加载状态
     */
    setForm({ ...form, isSubmitting: true });

    try {
      // 执行整体验证
      await validator.validate(valueMap);
    } catch (e) {
      // 验证失败，处理错误信息
      isValid = false;
      const err = e as ValidateErrorType;
      errors = err.fields; // 获取按字段分组的错误信息

      /**
       * 遍历所有字段，更新验证结果
       * 根据整体验证结果更新每个字段的验证状态
       */
      each(fields, (value, name) => {
        // errors 中有对应的 key，说明该字段验证失败
        if (errors[name]) {
          const itemErrors = errors[name];
          dispatch({
            type: "updateValidateResult",
            name,
            value: { isValid: false, errors: itemErrors },
          });
        } else if (value.rules.length > 0 && !errors[name]) {
          /**
           * 有对应的 rules，并且没有 errors，说明验证通过
           * 这种情况发生在：字段有验证规则，但在整体验证中没有错误
           */
          dispatch({
            type: "updateValidateResult",
            name,
            value: { isValid: true, errors: [] },
          });
        }
      });
    } finally {
      /**
       * 更新表单整体状态
       * 无论验证成功还是失败，都需要更新提交状态
       */
      setForm({ ...form, isSubmitting: false, isValid, errors });

      /**
       * 返回验证结果
       * 包含验证状态、错误信息和所有字段的值
       */
      return {
        isValid, // 整体验证是否通过
        errors, // 错误信息对象
        values: valueMap, // 所有字段的值
      };
    }
  };

  /**
   * 返回表单状态管理相关方法和状态
   * 这些方法和状态将被 Form 组件使用
   */
  return {
    fields, // 所有字段状态
    dispatch, // 字段状态分发函数
    form, // 表单整体状态
    validateField, // 验证单个字段
    getFieldValue, // 获取单个字段值
    validateAllFields, // 验证所有字段
    getFieldsValue, // 获取所有字段值
    setFieldValue, // 设置单个字段值
    resetFields, // 重置所有字段
  };
}

export default useStore;